/*
 * Thanks to
 * http://tips4java.wordpress.com/2009/07/05/hsl-color
 */

package com.myt.matp.utils;

public final class Color {

    private int m_RGB = 0;
    private float m_XYZ[] = new float[3];

    public Color(int rgb) {
	m_RGB = rgb;
    }

    public Color() {
	this(0);
    }


    /**
     *  Create a RGB Color object based on this HSLColor with a different
     *  Luminance value. The percent specified is an absolute value.
     *
     *  @paran percent - the Luminance value between 0 - 100
     *  @return the RGB Color object
     */
    public int adjustLuminance(float percent) {
	rgb2hsl(m_RGB, m_XYZ);
	m_RGB = hsl2rgb(m_XYZ[0], m_XYZ[1], percent); 
	return m_RGB;
    }
    
    /**
     *  Create a RGB Color object based on this HSLColor with a different
     *  Saturation value. The percent specified is an absolute value.
     *
     *  @paran percent - the Saturation value between 0 - 100
     *  @return the RGB Color object
     */
    public int adjustSaturation(float percent) {
	rgb2hsl(m_RGB, m_XYZ);
	m_RGB = hsl2rgb(m_XYZ[0], percent, m_XYZ[2]);
	return m_RGB;
    }

    /**
     *  Create a RGB Color object based on this HSLColor with a different
     *  Shade. Changing the shade will return a darker color. The percent
     *  specified is a relative value.
     *
     *  @paran percent - the value between 0 - 100
     *  @return the RGB Color object
     */
    public int adjustShade(float percent) {
	rgb2hsl(m_RGB, m_XYZ);
	float multiplier = (100.0f - percent) / 100.0f;
	float l = Math.max(0.0f, m_XYZ[2] * multiplier);
	m_RGB = hsl2rgb(m_XYZ[0], m_XYZ[1], l); 
	return m_RGB;
    }

    /**
     *  Create a RGB Color object based on this HSLColor with a different
     *  Tone. Changing the tone will return a lighter color. The percent
     *  specified is a relative value.
     *
     *  @paran percent - the value between 0 - 100
     *  @return the RGB Color object
     */
    public int adjustTone(float percent) {
	float multiplier = (100.0f + percent) / 100.0f;
	rgb2hsl(m_RGB, m_XYZ);
	float l = Math.min(100.0f, m_XYZ[2] * multiplier);
	m_RGB = hsl2rgb(m_XYZ[0], m_XYZ[1], l); 
	return m_RGB;
    }

    /**
     *  Create a RGB Color object that is the complimentary color of this
     *  HSLColor. This is a convenience method. The complimentary color is
     *  determined by adding 180 degrees to the Hue value.
     *  @return the RGB Color object
     */
    public int getComplimentary() {
	rgb2hsl(m_RGB, m_XYZ);
	float hue = (m_XYZ[0] + 180.0f) % 360.0f;
	m_RGB = hsl2rgb(hue, m_XYZ[1], m_XYZ[2]);
	return m_RGB;
    }
    

    public float[] getHSL() {
	return m_XYZ;
    }

    /**
     *  Get the RGB Color object represented by this HDLColor.
     *
     *  @return the RGB Color object.
     */
    public int getRGB() {
	return m_RGB;
    }

    /**
     *  Get the Hue value.
     *
     *  @return the Hue value.
     */
    public float getHue() {
	return m_XYZ[0];
    }

    /**
     *  Get the Saturation value.
     *
     *  @return the Saturation value.
     */
    public float getSaturation() {
	return m_XYZ[1];
    }

    /**
     *  Get the Luminance value.
     *
     *  @return the Luminance value.
     */
    public float getLuminance() {
	return m_XYZ[2];
    }

    public void setRGB(int rgb) {
	m_RGB = rgb;
    }

    public static int getRed(int rgb) {
	return ((rgb >> 16) & 0xFF);
    }

    public static int getGreen(int rgb) {
	return ((rgb >> 8) & 0xFF);
    }

    public static int getBlue(int rgb) { 
	return (rgb & 0xFF);
    }

    public static int getRGB(int r, int g, int b) {
	return ((r << 16) & 0xFF0000) | ((g << 8) & 0xFF00) | (b & 0xFF);
    }

    public static int getRGB(float r, float g, float b) {
	return getRGB((int)r, (int)g, (int)b);
    }

    public static void rgb2hsl(int rgb, float hsl[])
    {
	//  Get RGB values in the range 0 - 1

	float r = Color.getRed(rgb);
	float g = Color.getGreen(rgb);
	float b = Color.getBlue(rgb);

	//	Minimum and Maximum RGB values are used in the HSL calculations

	float min = Math.min(r, Math.min(g, b));
	float max = Math.max(r, Math.max(g, b));

	//  Calculate the Hue

	float h = 0;

	if (max == min)
	    h = 0;
	else if (max == r)
	    h = ((60 * (g - b) / (max - min)) + 360) % 360;
	else if (max == g)
	    h = (60 * (b - r) / (max - min)) + 120;
	else if (max == b)
	    h = (60 * (r - g) / (max - min)) + 240;

	//  Calculate the Luminance

	float l = (max + min) / 2;

	//  Calculate the Saturation

	float s = 0;

	if (max == min)
	    s = 0;
	else if (l <= .5f)
	    s = (max - min) / (max + min);
	else
	    s = (max - min) / (2 - max - min);

	hsl[0] = h;
	hsl[1] = s * 100;
	hsl[2] = l * 100;
    }

    public static int hsl2rgb (float h, float s, float l) {
	/*
	if (s <0.0f || s > 100.0f)
	    {
		String message = "Color parameter outside of expected range - Saturation";
		throw new IllegalArgumentException( message );
	    }

	if (l <0.0f || l > 100.0f)
	    {
		String message = "Color parameter outside of expected range - Luminance";
		throw new IllegalArgumentException( message );
	    }

	if (alpha <0.0f || alpha > 1.0f)
	    {
		String message = "Color parameter outside of expected range - Alpha";
		throw new IllegalArgumentException( message );
	    }
	*/

	//  Formula needs all values between 0 - 1.

	h = h % 360.0f;
	h /= 360f;
	s /= 100f;
	l /= 100f;

	float q = 0;

	if (l < 0.5)
	    q = l * (1 + s);
	else
	    q = (l + s) - (s * l);

	float p = 2 * l - q;

	float r = Math.max(0, HueToRGB(p, q, h + (1.0f / 3.0f)));
	float g = Math.max(0, HueToRGB(p, q, h));
	float b = Math.max(0, HueToRGB(p, q, h - (1.0f / 3.0f)));

	return getRGB(r, g, b);
    }

    private static float HueToRGB(float p, float q, float h) {
	if (h < 0) h += 1;

	if (h > 1 ) h -= 1;

	if (6 * h < 1)
	    {
		return p + ((q - p) * 6 * h);
	    }

	if (2 * h < 1 )
	    {
		return  q;
	    }

	if (3 * h < 2)
	    {
		return p + ( (q - p) * 6 * ((2.0f / 3.0f) - h) );
	    }

	return p;
    }

}